package com.chuangjiangx.member.stored.ddd.domain.service;

import com.chuangjiangx.commons.JacksonUtils;
import com.chuangjiangx.commons.exception.BaseException;
import com.chuangjiangx.member.basic.ddd.domain.model.MbrAccount;
import com.chuangjiangx.member.basic.ddd.domain.model.MbrAccountRepository;
import com.chuangjiangx.member.basic.ddd.domain.model.Member;
import com.chuangjiangx.member.basic.ddd.domain.model.MemberRepository;
import com.chuangjiangx.member.basic.ddd.domain.model.calc.AmountScoreGrandTotalCalc;
import com.chuangjiangx.member.basic.ddd.domain.service.MemberMsgDomainService;
import com.chuangjiangx.member.basic.ddd.domain.service.MemberRedisDomainService;
import com.chuangjiangx.member.stored.ddd.domain.MbrRandomUtils;
import com.chuangjiangx.member.basic.ddd.domain.model.MemberId;
import com.chuangjiangx.member.basic.ddd.domain.model.OperatorInfo;
import com.chuangjiangx.member.basic.ddd.domain.model.MerchantId;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderRefundStatus;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrStoredType;
import com.chuangjiangx.member.score.ddd.domain.model.MbrScoreType;
import com.chuangjiangx.domain.shared.model.PayEntry;
import com.chuangjiangx.member.score.ddd.domain.model.MbrScoreStream;
import com.chuangjiangx.member.score.ddd.domain.repository.MbrScoreStreamRepository;
import com.chuangjiangx.member.stored.ddd.domain.model.*;
import com.chuangjiangx.member.stored.ddd.domain.event.MbrRefundSuccessEvent;
import com.chuangjiangx.member.stored.ddd.domain.exception.MemberPaymentException;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderId;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderRefundId;
import com.chuangjiangx.member.common.utils.MbrKeyManager;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author cj-ch
 * @date 2018/5/24 下午7:09
 * 会员退款业务
 */
@Slf4j(topic = "pay")
@Service
public class MbrRefundService {
    private static final String REFUND_LOCK_PRE = "RD_L_";
    @Autowired
    private MemberRedisDomainService memberRedisDomainService;
    @Autowired
    private MbrScoreStreamRepository mbrScoreStreamRepository;
    @Autowired
    private MbrStoredStreamRepository mbrStoredStreamRepository;
    @Autowired
    private MbrAccountRepository mbrAccountRepository;
    @Autowired
    private MemberRepository memberRepository;
    @Autowired
    private MbrOrderRepository mbrOrderRepository;
    @Autowired
    private MemberMsgDomainService memberMsgDomainService;
    @Autowired
    private MbrOrderRefundRepository mbrOrderRefundRepository;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ObjectMapper objectMapper;

    public void refund(MbrRefundSuccessEvent event){
        if(BigDecimal.ZERO.compareTo(event.getCurrentRefundAmout()) >= 0){
            return;
        }
        if(Objects.equals(PayEntry.MSCARDPAY,event.getPayEntry())){
            mbrCardRefund(event);
        }else{
            otherRefund(event);
        }
    }

    /**
     * 会员卡退款
     * @param event
     */
    public void mbrCardRefund(MbrRefundSuccessEvent event){
        if(!mbrCardRefundCheck(event)){
            return;
        }
        final OperatorInfo operatorInfo = event.getOperatorInfo();

        String lockKey = REFUND_LOCK_PRE + event.getMbrOrderRefundId();
        if(!memberRedisDomainService.lock(lockKey, String.valueOf(System.currentTimeMillis()),5L,TimeUnit.MINUTES)){
            log.info("会员卡退款业务获取锁失败:{}",event.toString());
            return;
        }
        Member member = memberRepository.fromId(new MemberId(event.getMemberId()));
        if(null == member){
            throw new MemberPaymentException("","会员不存在");
        }
        MbrAccount mbrAccount = mbrAccountRepository.fromMemberId(member.getId());
        if(null == member){
            throw new MemberPaymentException("","会员账户不存在");
        }
        MbrOrder order = mbrOrderRepository.fromId(new MbrOrderId(event.getMbrOrderId()));

        //获取退款订单信息
        MbrOrderRefund refund = fetchMbrRefundOrder(event);

        MbrStoredStream mbrStoredStream = recordMbrStoredStream(event,order, member, mbrAccount, refund);

        //查询消费时产生的储值记录
        MbrStoredStream mbrStoredStreamHis = mbrStoredStreamRepository.from(order.getId().getId(), MbrStoredType.CONSUMER);
        if(null != mbrStoredStreamHis){
            //查询消费时产生的积分记录
            MbrScoreStream mbrScoreStreamHis = mbrScoreStreamRepository.fromMbrStoreStreamIdAndType(mbrStoredStreamHis.getId().getId(), MbrScoreType.CONSUMER);
            if(null != mbrScoreStreamHis){
                //计算应该扣除的积分
                Long deductionScore = new AmountScoreGrandTotalCalc()
                        .subtractScoreCalc(event.getCurrentRefundAmout(),event.getRealPayAmount(), mbrScoreStreamHis.getScore());
                if(0 < deductionScore){
                    String scoreRemark = "退款"+event.getCurrentRefundAmout().stripTrailingZeros().toPlainString()+"元扣除积分";
                    //账户扣除积分
                    mbrAccount.changeScore(deductionScore,MbrScoreType.REFUND);
                    //记录积分流水
                    MbrScoreStream mbrScoreStream = new MbrScoreStream(
                            member.getId(),
                            deductionScore,
                            MbrScoreType.REFUND,
                            mbrStoredStream.getId(),
                            null,
                            null,
                            null,
                            mbrAccount.getAvailableScore(),
                            scoreRemark,
                            null,
                            operatorInfo.getMerchantUserId(),
                            operatorInfo.getStoreUserId(),
                            operatorInfo.getStoreId()
                    );
                    mbrScoreStreamRepository.save(mbrScoreStream);
                    //消息推送
                    memberMsgDomainService.sendMbrScoreChangeMsg(member.getOperationInfo().getMerchantId().getId(), mbrAccount, mbrScoreStream,
                            "退款扣除");
                }
            }
        }

        //保存账户数据
        mbrAccountRepository.update(mbrAccount);
        memberRedisDomainService.delKey(lockKey);
    }

    protected MbrOrderRefund fetchMbrRefundOrder(MbrRefundSuccessEvent event) {
        MbrOrderRefund refund;
        String json = (String) redisTemplate.opsForValue().get(MbrKeyManager.MBR_ORDER_REFUND_ID_KEY + event.getMbrOrderRefundId());
        if(StringUtils.isNotBlank(json)){
            MbrOrderRefundMirror mirror = JacksonUtils.toObject(objectMapper, json, MbrOrderRefundMirror.class);
            refund = mirror.convertToMbrOrderRefund();
        } else {
            refund = mbrOrderRefundRepository.fromId(new MbrOrderRefundId(event.getMbrOrderRefundId()));
        }
        if(refund == null){
            throw new BaseException("","获取退款订单信息失败!订单ID:" + event.getMbrOrderRefundId());
        }
        return refund;
    }


    /**
     * 会员卡退款后续业务进行时的前置条件检查
     * @param event
     * @return
     */
    public boolean mbrCardRefundCheck(MbrRefundSuccessEvent event){
        MbrStoredStream mbrStoredStream = mbrStoredStreamRepository.fromMbrRefundId(event.getMbrOrderRefundId(), MbrStoredType.REFUND);
        if(null != mbrStoredStream){
            return false;
        }
        return true;
    }

    /**
     * 记录储值流水
     * 账户储值变动,新增储值流水,发送消息
     */
    private MbrStoredStream recordMbrStoredStream(MbrRefundSuccessEvent event,MbrOrder order, Member member, MbrAccount mbrAccount, MbrOrderRefund refund) {
        final OperatorInfo operatorInfo = event.getOperatorInfo();
        //账户余额变动
        mbrAccount.changeMoney(refund.getRefundAmount(),MbrStoredType.REFUND);
        //新增储值流水记录
        MbrStoredStream mbrStoredStream = new MbrStoredStream(
                member.getId(),
                refund.getRefundAmount(),
                mbrAccount.getAvailableBalance(),
                MbrStoredType.REFUND,
                null,
                "退款退回",
                order.getId(),
                refund.getId(),
                operatorInfo.getMerchantUserId(),
                operatorInfo.getStoreUserId(),
                operatorInfo.getStoreId()
        );
        mbrStoredStreamRepository.save(mbrStoredStream);
        //消息推送
        memberMsgDomainService.sendMbrCardRefundMsg(member, mbrAccount, refund,
                mbrStoredStream);
        return mbrStoredStream;
    }


    /**
     * 其他支付退款
     * @param event
     */
    public void otherRefund(MbrRefundSuccessEvent event){
        // 非会员卡退款
        if(!otherRefundCheck(event)){
            return;
        }
        String lockKey = REFUND_LOCK_PRE + event.getOrderRefundNumber();
        if(!memberRedisDomainService.lock(lockKey, String.valueOf(System.currentTimeMillis()),5L,TimeUnit.MINUTES)){
            log.info("非会员卡支付退款业务获取锁失败:{}",event.toString());
            return;
        }
        final OperatorInfo operatorInfo = event.getOperatorInfo();
        //退积分
        scoreRefund(event, operatorInfo);

        //同步退款信息
        if (StringUtils.isNotBlank(event.getOrderPayNumber())) {
            MbrOrder mbrOrder = mbrOrderRepository.fromOrderPayNumber(event.getOrderPayNumber());
            if (mbrOrder != null) {
                MbrOrderRefund mbrOrderRefund = new MbrOrderRefund(
                        mbrOrder.getId(),
                        MbrRandomUtils.generateMbrRefundOrderNumber(),
                        new MerchantId(event.getMerchantId()),
                        event.getMerchantUserId(),
                        mbrOrder.getMemberId(),
                        operatorInfo.getStoreId(),
                        operatorInfo.getStoreUserId(),
                        MbrOrderRefundStatus.SUCCESSFUL,
                        event.getCurrentRefundAmout(),
                        new Date(),
                        event.getPayTerminal()
                );
                //
                mbrOrderRefundRepository.save(mbrOrderRefund);
                // 判断订单支付状态
                mbrOrder.refund(mbrOrderRefund);
                mbrOrderRepository.update(mbrOrder);
            }
        }
    }

    /**
     * 退积分
     * @param event
     * @param operatorInfo
     * @return
     */
    protected Member scoreRefund(MbrRefundSuccessEvent event, OperatorInfo operatorInfo) {
        //查询历史积分记录
        MbrScoreStream mbrScoreStreamHis = mbrScoreStreamRepository.fromPayOrderNumberAndType(event.getOrderPayNumber(), MbrScoreType.CONSUMER);
        if(null == mbrScoreStreamHis){
            return null;
        }
        Member member = memberRepository.fromId(mbrScoreStreamHis.getMemberId());
        if(null == member){
            throw new MemberPaymentException("","会员不存在");
        }
        MbrAccount mbrAccount = mbrAccountRepository.fromMemberId(member.getId());
        if(null == member){
            throw new MemberPaymentException("","会员账户不存在");
        }
        //计算应该扣除的积分
        long deductionScore = new AmountScoreGrandTotalCalc()
                .subtractScoreCalc(event.getCurrentRefundAmout(),event.getRealPayAmount(), mbrScoreStreamHis.getScore());

        if(0 < deductionScore){
            String scoreRemark = "退款"+event.getCurrentRefundAmout().stripTrailingZeros().toPlainString()+"元扣除积分";
            //账户扣除积分
            mbrAccount.changeScore(deductionScore,MbrScoreType.REFUND);
            //记录积分流水
            MbrScoreStream mbrScoreStream = new MbrScoreStream(
                    member.getId(),
                    deductionScore,
                    MbrScoreType.REFUND,
                    null,
                    null,
                    event.getOrderRefundNumber(),
                    event.getOrderPayNumber(),
                    mbrAccount.getAvailableScore(),
                    scoreRemark,
                    null,
                    operatorInfo.getMerchantUserId(),
                    operatorInfo.getStoreUserId(),
                    operatorInfo.getStoreId()
            );

            mbrScoreStreamRepository.save(mbrScoreStream);
            //消息推送
            memberMsgDomainService.sendMbrScoreChangeMsg(member.getOperationInfo().getMerchantId().getId(), mbrAccount, mbrScoreStream,
                    "退款扣除");
        }
        //保存账户数据
        mbrAccountRepository.update(mbrAccount);
        return member;
    }

    /**
     * 非会员卡支付退款后续业务进行时的前置条件检查
     * @param event
     * @return
     */
    public boolean otherRefundCheck(MbrRefundSuccessEvent event){
        //判断支付渠道是否支持支付即会员
//        if(!MemberSupport.supportPayIsMember(event.getPayChannelId())){
//            return false;
//        }
        MbrScoreStream mbrScoreStream = mbrScoreStreamRepository.fromOrderRefundNumAndType(event.getOrderRefundNumber(), MbrScoreType.REFUND);
        if(null != mbrScoreStream){
            return false;
        }
        return true;
    }
}
